# -*- coding: utf-8 -*-
#
#  Copyright (C) 2015 by Ihor E. Novikov
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <https://www.gnu.org/licenses/>.

import os
import shutil

import wal
from generic import PrefPanel
from sk1 import _, config, dialogs
from sk1.resources import icons, get_bmp
from uc2.cms import get_profile_name, get_profile_descr
from uc2.uc2const import COLOR_RGB, COLOR_CMYK, COLOR_LAB, \
    COLOR_GRAY, COLOR_DISPLAY, ICC, ICM, INTENTS
from uc2.utils import fsutils

COLORSPACES = [COLOR_RGB, COLOR_CMYK, COLOR_GRAY, COLOR_DISPLAY]


class CMSPrefs(PrefPanel):
    pid = 'CMS'
    name = _('CMS')
    title = _('Color management, profiles')
    icon_id = icons.PD_PREFS_CMS
    tabs = []

    nb = None
    page0 = None
    page1 = None
    page2 = None

    def __init__(self, app, dlg, *_args):
        PrefPanel.__init__(self, app, dlg)

    def build(self):
        self.nb = wal.Notebook(self)

        # ========Color management options
        self.page0 = CmsOptions(self.nb, self)
        self.page1 = CmsProfiles(self.nb, self)
        self.page2 = CmsSettings(self.nb, self)

        for item in [self.page0, self.page1, self.page2]:
            self.nb.add_page(item, item.name)

        self.pack(self.nb, expand=True, fill=True)
        self.built = True
        self.page0.activate_cms()

    def apply_changes(self):
        for item in self.nb.childs:
            item.apply_changes()

    def restore_defaults(self):
        for item in self.nb.childs:
            item.restore_defaults()


class CmsTab(wal.VPanel):
    name = ''
    prefpanel = None

    def __init__(self, parent, prefpanel):
        self.prefpanel = prefpanel
        wal.VPanel.__init__(self, parent)

    def apply_changes(self): pass

    def restore_defaults(self): pass


class CmsOptions(CmsTab):
    name = _('Color management')

    def __init__(self, parent, prefpanel):
        CmsTab.__init__(self, parent, prefpanel)
        txt = _('Enable Color Management')
        panel = wal.VPanel(self)
        hp = wal.HPanel(panel)
        hp.pack(wal.Label(hp, txt))
        hp.pack((1, 1), expand=True)
        self.cms_check = wal.Switch(hp, config.cms_use,
                                    onclick=self.activate_cms)
        hp.pack(self.cms_check, end_padding=1)
        panel.pack(hp, fill=True, padding_all=5)

        self.banner = wal.VPanel(panel)
        self.banner.set_bg(wal.DARK_GRAY)
        bmp = get_bmp(self.banner, icons.PD_PREFS_CMS_BANNER)
        self.banner.pack(bmp, padding=2)
        panel.pack(self.banner, fill=True, expand=True)
        txt = _('Note: If Color Management is disabled, '
                'color values will be inaccurate.')
        label = wal.Label(panel, txt, fontsize=-1)
        panel.pack(label, fill=True, padding_all=5)
        self.pack(panel, fill=True, expand=True)

    def activate_cms(self):
        self.banner.set_visible(self.cms_check.get_value())
        nb = self.prefpanel.nb
        if self.cms_check.get_value() and len(nb.childs) < 2:
            nb.add_page(self.prefpanel.page1, self.prefpanel.page1.name)
            nb.add_page(self.prefpanel.page2, self.prefpanel.page2.name)
        elif not self.cms_check.get_value() and len(nb.childs) > 1:
            nb.remove_page(self.prefpanel.page1)
            nb.remove_page(self.prefpanel.page2)

    def apply_changes(self):
        config.cms_use = self.cms_check.get_value()

    def restore_defaults(self):
        defaults = config.get_defaults()
        self.cms_check.set_value(defaults['cms_use'])


class CmsProfiles(CmsTab):
    name = _('Color profiles')

    def __init__(self, parent, prefpanel):
        CmsTab.__init__(self, parent, prefpanel)

        txt = _('Color space profiles')
        self.pack(wal.Label(self, txt, fontbold=True), padding=2)
        self.pack(wal.HLine(self), fill=True, padding_all=2)

        grid = wal.GridPanel(self, rows=10, cols=3, hgap=5, vgap=5)
        grid.add_growable_col(1)

        self.cs_widgets = {}
        self.cs_profiles = {}
        self.cs_config_profiles = {}

        self.cs_config = {COLOR_RGB: config.cms_rgb_profile,
                          COLOR_CMYK: config.cms_cmyk_profile,
                          COLOR_LAB: config.cms_lab_profile,
                          COLOR_GRAY: config.cms_gray_profile,
                          COLOR_DISPLAY: config.cms_display_profile}

        for colorspace in COLORSPACES[:-1]:
            txt = _('%s profile:') % colorspace
            grid.pack(wal.Label(grid, txt))
            combo = wal.Combolist(grid, items=[])
            self.cs_widgets[colorspace] = combo
            grid.pack(combo, fill=True)
            self.update_combo(colorspace)
            grid.pack(ManageButton(grid, self, colorspace))

        self.pack(grid, fill=True, padding_all=5)

        txt = _('Hardware profiles')
        self.pack(wal.Label(self, txt, fontbold=True), padding=2)
        self.pack(wal.HLine(self), fill=True, padding_all=2)

        grid = wal.GridPanel(self, cols=3, hgap=5, vgap=5)
        grid.add_growable_col(1)

        txt = _('Display profile:')
        grid.pack(wal.Label(grid, txt))
        combo = wal.Combolist(grid, items=[])
        self.cs_widgets[COLOR_DISPLAY] = combo
        grid.pack(combo, fill=True)
        self.update_combo(COLOR_DISPLAY)
        grid.pack(ManageButton(grid, self, COLOR_DISPLAY))

        self.pack(grid, fill=True, padding_all=5)

        txt = _('Use display profile')
        self.display_check = wal.Checkbox(self, txt,
                                          config.cms_use_display_profile,
                                          onclick=self.activate_display)
        self.pack(self.display_check, align_center=False)
        # self.pack(wal.HLine(self), fill=True, padding_all=2)
        #
        # txt = _('Note: Display profile for your hardware can be found here:')
        # label = wal.Label(self, txt, fontsize=-1)
        # self.pack(label, fill=True, padding_all=5)
        # self.pack(wal.HyperlinkLabel(self, 'https://icc.opensuse.org/'))
        self.activate_display()

    def activate_display(self):
        combo = self.cs_widgets[COLOR_DISPLAY]
        combo.set_enable(self.display_check.get_value())

    def update_config_data(self, colorspace):
        if colorspace == COLOR_RGB:
            self.cs_config_profiles[colorspace] = config.cms_rgb_profiles.copy()
        elif colorspace == COLOR_CMYK:
            self.cs_config_profiles[
                colorspace] = config.cms_cmyk_profiles.copy()
        elif colorspace == COLOR_LAB:
            self.cs_config_profiles[colorspace] = config.cms_lab_profiles.copy()
        elif colorspace == COLOR_GRAY:
            self.cs_config_profiles[
                colorspace] = config.cms_gray_profiles.copy()
        else:
            self.cs_config_profiles[
                colorspace] = config.cms_display_profiles.copy()
        self.cs_profiles[colorspace] = self.get_profile_names(colorspace)

    def update_combo(self, colorspace, set_active=True):
        self.update_config_data(colorspace)
        combo = self.cs_widgets[colorspace]
        combo.set_items(self.cs_profiles[colorspace])
        if not set_active:
            return
        self.set_active_profile(combo, self.cs_config[colorspace], colorspace)

    def set_active_profile(self, widget, name, colorspace):
        profiles = self.get_profile_names(colorspace)
        if not name:
            widget.set_active(0)
        elif name not in profiles:
            widget.set_active(0)
            if colorspace == COLOR_RGB:
                config.cms_rgb_profile = ''
            elif colorspace == COLOR_CMYK:
                config.cms_cmyk_profile = ''
            elif colorspace == COLOR_LAB:
                config.cms_lab_profile = ''
            elif colorspace == COLOR_GRAY:
                config.cms_gray_profile = ''
            else:
                config.cms_display_profile = ''
        else:
            widget.set_active(profiles.index(name))

    def get_profile_names(self, colorspace):
        names = []
        default = _('Built-in %s profile') % colorspace
        names.append(default)
        names += self.cs_config_profiles[colorspace].keys()
        return names

    def apply_changes(self):
        for colorspace in COLORSPACES:
            profiles = self.get_profile_names(colorspace)
            combo = self.cs_widgets[colorspace]
            index = combo.get_active()
            profile_name = ''
            if index:
                profile_name = profiles[index]
            if colorspace == COLOR_RGB:
                config.cms_rgb_profile = profile_name
            elif colorspace == COLOR_CMYK:
                config.cms_cmyk_profile = profile_name
            elif colorspace == COLOR_LAB:
                config.cms_lab_profile = profile_name
            elif colorspace == COLOR_GRAY:
                config.cms_gray_profile = profile_name
            else:
                config.cms_display_profile = profile_name
        config.cms_use_display_profile = self.display_check.get_value()

    def restore_defaults(self):
        for item in COLORSPACES:
            self.cs_config[item] = ''
            self.update_combo(item)
        defaults = config.get_defaults()
        self.display_check.set_value(defaults['cms_use_display_profile'])


class ManageButton(wal.ImageButton):
    colorspace = ''
    owner = None

    def __init__(self, parent, owner, colorspace):
        self.owner = owner
        self.colorspace = colorspace
        txt = _('Add/remove %s profiles') % colorspace
        art_size = wal.DEF_SIZE
        decoration_padding = 6
        if wal.IS_MSW:
            decoration_padding = 4
        wal.ImageButton.__init__(self, parent, icons.PD_PREFS_EDIT,
                                 art_size=art_size,
                                 decoration_padding=decoration_padding,
                                 tooltip=txt, flat=False, onclick=self.action)

    def action(self):
        app = self.owner.prefpanel.app
        dlg = self.owner.prefpanel.dlg
        ProfileManager(app, dlg, self.colorspace).show()
        combo = self.owner.cs_widgets[self.colorspace]
        profiles = self.owner.cs_profiles[self.colorspace]
        selected = profiles[combo.get_active()]
        self.owner.update_combo(self.colorspace)
        profiles = self.owner.cs_profiles[self.colorspace]
        if selected in profiles:
            combo.set_active(profiles.index(selected))


class ProfileManager(wal.CloseDialog):
    profiles = {}
    pf_list = []

    viewer = None
    import_btn = None
    remove_btn = None
    info_btn = None

    def __init__(self, app, parent, colorspace):
        self.app = app
        self.colorspace = colorspace
        size = (400, 250)
        title = _('%s profiles') % colorspace
        wal.CloseDialog.__init__(self, parent, title, size, wal.HORIZONTAL)

    def set_profiles(self):
        if self.colorspace == COLOR_RGB:
            self.profiles = config.cms_rgb_profiles.copy()
        elif self.colorspace == COLOR_CMYK:
            self.profiles = config.cms_cmyk_profiles.copy()
        elif self.colorspace == COLOR_LAB:
            self.profiles = config.cms_lab_profiles.copy()
        elif self.colorspace == COLOR_GRAY:
            self.profiles = config.cms_gray_profiles.copy()
        else:
            self.profiles = config.cms_display_profiles.copy()
        self.update_list()

    def save_profiles(self):
        if self.colorspace == COLOR_RGB:
            config.cms_rgb_profiles = self.profiles
        elif self.colorspace == COLOR_CMYK:
            config.cms_cmyk_profiles = self.profiles
        elif self.colorspace == COLOR_LAB:
            config.cms_lab_profiles = self.profiles
        elif self.colorspace == COLOR_GRAY:
            config.cms_gray_profiles = self.profiles
        else:
            config.cms_display_profiles = self.profiles
        self.update_list()

    def update_list(self):
        keys = self.profiles.keys()
        keys.sort()
        default = _('Built-in %s profile') % self.colorspace
        self.pf_list = [default, ] + keys

    def build(self):
        self.set_profiles()
        vp = wal.VPanel(self.panel)
        vp.set_bg(wal.UI_COLORS['border'])
        self.viewer = wal.SimpleList(vp, self.pf_list,
                                     on_select=self.selection_changed)
        vp.pack(self.viewer, expand=True, fill=True, padding_all=1)
        self.panel.pack(vp, expand=True, fill=True, padding_all=5)
        btn_box = wal.VPanel(self.panel)
        self.panel.pack(btn_box, fill=True, padding_all=5)

        self.import_btn = wal.Button(btn_box, _('Import'),
                                     onclick=self.import_profile)
        btn_box.pack(self.import_btn, fill=True, end_padding=5)
        self.remove_btn = wal.Button(btn_box, _('Remove'),
                                     onclick=self.remove_profile)
        btn_box.pack(self.remove_btn, fill=True, end_padding=5)
        self.info_btn = wal.Button(btn_box, _('Info'),
                                   onclick=self.profile_info)
        btn_box.pack(self.info_btn, fill=True, end_padding=5)
        self.viewer.set_active(0)

    def selection_changed(self, _ret):
        index = self.viewer.get_active()
        self.remove_btn.set_enable(index not in (0, -1))
        self.info_btn.set_enable(index not in (0, -1))

    def import_profile(self):
        src = dialogs.get_open_file_name(self, config.profile_import_dir,
                                         _('Select profile to import'),
                                         file_types=[ICC, ICM])
        if not src:
            return
        name = get_profile_name(src)
        title = self.app.appdata.app_name
        if name is None:
            msg = _('Cannot open profile')
            msg = "%s '%s'" % (msg, src)
            sec = _('The profile may be corrupted or in an unsupported format')
            wal.error_dialog(self, title, msg + '\n' + sec)
            return
        if name in self.pf_list:
            msg = _('Selected profile cannot be added to profile list:')
            msg = "%s '%s'" % (msg, name)
            sec = _('It seems like you have already imported this profile')
            wal.error_dialog(self, title, msg + '\n' + sec)
            return
        filename = os.path.basename(src)
        dst_dir = self.app.appdata.app_color_profile_dir
        dst = os.path.join(dst_dir, filename)
        if fsutils.exists(dst):
            msg = _('Selected file has been added to profile pool')
            msg = "%s '%s'" % (msg, src)
            sec = _('If you still wish to import the file, try renaming it')
            wal.error_dialog(self, title, msg + '\n' + sec)
            return
        try:
            fsutils.copy(src, dst)
        except Exception:
            msg = _('Cannot copy file')
            msg = "%s '%s'" % (msg, src)
            sec = _('Please check write permissions for config directory:')
            sec += '\n%s' % dst_dir
            wal.error_dialog(self, title, msg + '\n' + sec)
            return
        config.profile_import_dir = os.path.dirname(src)
        self.profiles[name] = filename
        self.apply_changes()
        self.viewer.set_active(self.pf_list.index(name))

    def remove_profile(self):
        index = self.viewer.get_active()
        name = self.pf_list[index]
        filename = self.profiles[name]
        dst_dir = self.app.appdata.app_color_profile_dir
        dst = os.path.join(dst_dir, filename)
        if fsutils.isfile(dst):
            fsutils.remove(dst)
        self.profiles.pop(name)
        self.apply_changes()
        self.viewer.set_active(index - 1)

    def apply_changes(self):
        self.save_profiles()
        self.viewer.update(self.pf_list)

    def profile_info(self):
        index = self.viewer.get_active()
        name = self.pf_list[index]
        filename = self.profiles[name]
        dst_dir = self.app.appdata.app_color_profile_dir
        dst = os.path.join(dst_dir, filename)
        if fsutils.isfile(dst):
            ProfileInfoViewer(self, dst).show()


class ProfileInfoViewer(wal.CloseDialog):
    def __init__(self, parent, filepath):
        self.filepath = filepath
        size = (400, 250)
        title = _('Profile info')
        wal.CloseDialog.__init__(self, parent, title, size, wal.HORIZONTAL)

    def build(self):
        name, copyrigth, info = get_profile_descr(self.filepath)
        filename = os.path.basename(self.filepath)
        copyrigth = copyrigth or '--'
        info = info or '--'
        grid = wal.GridPanel(self.panel, rows=4, cols=2, vgap=5, hgap=5)
        grid.add_growable_col(1)
        grid.add_growable_row(2)
        grid.add_growable_row(3)
        grid.pack(wal.Label(grid, _('Name:')))
        grid.pack(wal.Label(grid, name, True))
        grid.pack(wal.Label(grid, _('File:')))
        grid.pack(wal.Label(grid, filename))

        panel = wal.VPanel(grid)
        panel.pack(wal.Label(grid, _('Copyright:')), align_center=False)
        grid.pack(panel, fill=True)

        grid.pack(wal.Entry(grid, copyrigth, multiline=True, editable=False),
                  fill=True)

        panel = wal.VPanel(grid)
        panel.pack(wal.Label(grid, _('Description:')), align_center=False)
        grid.pack(panel, fill=True)

        grid.pack(wal.Entry(grid, info, multiline=True, editable=False),
                  fill=True)
        self.panel.pack(grid, fill=True, expand=True)


class CmsSettings(CmsTab):
    name = _('Settings')

    def __init__(self, parent, prefpanel):
        CmsTab.__init__(self, parent, prefpanel)

        self.intents = INTENTS.keys()
        self.intents.sort()
        self.intents_names = []
        for item in self.intents:
            self.intents_names.append(INTENTS[item])

        panel = wal.VPanel(self.panel)

        # Intents panel
        int_panel = wal.LabeledPanel(panel, _('Rendering intents'))
        grid = wal.GridPanel(int_panel, vgap=5, hgap=5)

        grid.pack(wal.Label(grid, _('Display/RGB intent:')))
        self.display = wal.Combolist(grid, items=self.intents_names)
        self.display.set_active(self.intents.index(config.cms_rgb_intent))
        grid.pack(self.display)

        grid.pack(wal.Label(grid, _('Printer/CMYK intent:')))
        self.printer = wal.Combolist(grid, items=self.intents_names)
        self.printer.set_active(self.intents.index(config.cms_cmyk_intent))
        grid.pack(self.printer)

        int_panel.pack(grid, align_center=False, padding_all=10)
        panel.pack(int_panel, fill=True)

        # Simulate printer panel
        txt = _('Simulate printer on the screen')
        self.simulate_check = wal.Checkbox(panel, txt,
                                           config.cms_proofing,
                                           onclick=self.activate_simulation)

        sm_panel = wal.LabeledPanel(panel, widget=self.simulate_check)

        txt = _("Highlight colors that are outside of the printer's gamut")
        self.outcolors_check = wal.Checkbox(sm_panel, txt,
                                            config.cms_gamutcheck,
                                            onclick=self.activate_outcolors)
        sm_panel.pack(self.outcolors_check, align_center=False, padding_all=5)

        clrpanel = wal.HPanel(sm_panel)
        clrpanel.pack((20, 1))
        self.alarm_label = wal.Label(clrpanel, _('Highlight color:'))
        clrpanel.pack(self.alarm_label, padding=5)
        self.color_btn = wal.ColorButton(clrpanel, config.cms_alarmcodes)
        clrpanel.pack(self.color_btn)
        sm_panel.pack(clrpanel, align_center=False, padding_all=2)

        txt = _('Separation for SPOT colors')
        self.separation_check = wal.Checkbox(sm_panel, txt,
                                             config.cms_proof_for_spot,
                                             onclick=self.activate_outcolors)
        sm_panel.pack(self.separation_check, align_center=False, padding_all=5)

        panel.pack(sm_panel, fill=True, padding=5)

        # Bottom checks
        txt = _('Use Blackpoint Compensation')
        self.bpc_check = wal.Checkbox(panel, txt,
                                      config.cms_bpc_flag)
        panel.pack(self.bpc_check, align_center=False)

        if wal.IS_MSW:
            panel.pack((5, 5))

        txt = _('Use Black preserving transforms')
        self.bpt_check = wal.Checkbox(panel, txt,
                                      config.cms_bpt_flag)
        panel.pack(self.bpt_check, align_center=False)

        self.panel.pack(panel, fill=True, padding_all=5)
        self.activate_simulation()

    def activate_simulation(self):
        self.outcolors_check.set_enable(self.simulate_check.get_value())
        self.separation_check.set_enable(self.simulate_check.get_value())
        self.activate_outcolors()

    def activate_outcolors(self):
        val = False
        if self.outcolors_check.get_enabled():
            val = self.outcolors_check.get_value()
        self.alarm_label.set_enable(val)
        self.color_btn.set_enable(val)

    def apply_changes(self):
        config.cms_rgb_intent = self.intents[self.display.get_active()]
        config.cms_cmyk_intent = self.intents[self.printer.get_active()]
        config.cms_proofing = self.simulate_check.get_value()
        config.cms_gamutcheck = self.outcolors_check.get_value()
        config.cms_proof_for_spot = self.separation_check.get_value()
        config.cms_alarmcodes = self.color_btn.get_value()
        config.cms_bpc_flag = self.bpc_check.get_value()
        config.cms_bpt_flag = self.bpt_check.get_value()

    def restore_defaults(self):
        defaults = config.get_defaults()
        self.display.set_active(self.intents.index(defaults['cms_rgb_intent']))
        self.printer.set_active(self.intents.index(defaults['cms_cmyk_intent']))
        self.simulate_check.set_value(defaults['cms_proofing'])
        self.outcolors_check.set_value(defaults['cms_gamutcheck'])
        self.separation_check.set_value(defaults['cms_proof_for_spot'])
        self.color_btn.set_value(defaults['cms_alarmcodes'])
        self.bpc_check.set_value(defaults['cms_bpc_flag'])
        self.bpt_check.set_value(defaults['cms_bpt_flag'])
